package nl.Viewer.Dialogs;

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

import nl.Viewer.Application;
import nl.Viewer.GUI;
import nl.Viewer.Dialogs.DatasetSelection.DatasetSelectionDialog;
import nl.Viewer.SerializedObjects.Dataset;
import nl.Viewer.Sessions.SessionManager;

/**
 * A pretty basic login dialog which allows the user to enter a username and password which is then verified with a
 * provided server (with address, port and soap endpoint)
 * 
 * @author Jeroen Verschuur
 * 
 */

public class LoginDialog extends JDialog implements ActionListener, KeyListener {
   private static final long serialVersionUID = -1;
   private JPasswordField    passwordField;
   private JTextField        usernameField;
   private JTextField        locationField;
   private String            password         = null;
   private String            username         = null;
   private String            server           = null;
   private int               port;
   private String            endpoint         = null;
   private boolean           isCanceled       = false;
   private JButton           BTN_OK;
   private JButton           BTN_CANCEL;
   private SessionManager    sessionManager;
   private GUI               gui;
   private Dataset           selectedDataset;

   public LoginDialog( JFrame owner, GUI gui, SessionManager sessionManager ) {
      super( owner, "Login", true );
      this.gui = gui;
      this.sessionManager = sessionManager;

      this.setLayout( new GridBagLayout() );

      // this.setSize(new Dimension(width, height));
      initializeComponents();
      this.pack();
      this.setMinimumSize( this.getSize() );

      this.setLocationRelativeTo( owner );
      resetDefaultValues();
   }

   private void initializeComponents() {
      GridBagConstraints c = new GridBagConstraints();

      // main panel
      JPanel mainPanel = new JPanel( new GridBagLayout() );
      c.gridx = 0;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.CENTER;
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 1;
      c.weighty = 1;
      c.insets = new Insets( 0, 0, 0, 0 );

      this.getContentPane().add( mainPanel, c );

      c.gridx = 0;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 1;
      c.weighty = 1;
      c.insets = new Insets( 4, 4, 4, 4 );

      mainPanel.add( new JLabel( "Specify login information:" ), c );

      JPanel fields = new JPanel( new GridBagLayout() );

      // user label and textfield
      c.gridx = 0;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 0;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      fields.add( new JLabel( "Username:" ), c );

      c.gridx = 1;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      usernameField = new JTextField( "jeroen", 25 );
      usernameField.setPreferredSize( new Dimension( 150, 20 ) );

      usernameField.addKeyListener( this );

      fields.add( usernameField, c );

      // password label and textfield
      c.gridx = 0;
      c.gridy = 1;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 0;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      fields.add( new JLabel( "Password:" ), c );

      c.gridx = 1;
      c.gridy = 1;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      passwordField = new JPasswordField( "jeroen", 25 );
      passwordField.setPreferredSize( new Dimension( 150, 20 ) );

      passwordField.addKeyListener( this );

      fields.add( passwordField, c );

      // server location label and textfield
      c.gridx = 0;
      c.gridy = 2;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 0;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      fields.add( new JLabel( "Location:" ), c );

      c.gridx = 1;
      c.gridy = 2;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.HORIZONTAL;
      c.weightx = 1;
      c.weighty = 0;
      c.insets = new Insets( 4, 4, 4, 4 );

      locationField = new JTextField( Application.serverAddress, 25 );
      locationField.setPreferredSize( new Dimension( 150, 20 ) );

      locationField.addKeyListener( this );

      fields.add( locationField, c );

      JPanel buttons = new JPanel( new GridBagLayout() );

      BTN_OK = new JButton( "Login" );

      BTN_OK.addActionListener( this );

      c.gridx = 0;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.SOUTHEAST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 1;
      c.weighty = 1;
      c.insets = new Insets( 0, 0, 0, 0 );

      buttons.add( BTN_OK, c );

      BTN_CANCEL = new JButton( "Cancel" );
      BTN_CANCEL.addActionListener( this );
      c.gridx = 1;
      c.gridy = 0;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.SOUTHEAST;
      c.fill = GridBagConstraints.NONE;
      c.weightx = 0;
      c.weighty = 1;
      c.insets = new Insets( 0, 10, 0, 0 );

      buttons.add( BTN_CANCEL, c );

      c.gridx = 0;
      c.gridy = 1;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.NORTHWEST;
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 1;
      c.weighty = 1;
      c.insets = new Insets( 10, 0, 0, 0 );
      mainPanel.add( fields, c );

      c.gridx = 0;
      c.gridy = 2;
      c.gridwidth = 1;
      c.gridheight = 1;
      c.anchor = GridBagConstraints.SOUTHEAST;
      c.fill = GridBagConstraints.BOTH;
      c.weightx = 1;
      c.weighty = 1;
      c.insets = new Insets( 4, 4, 4, 4 );
      mainPanel.add( buttons, c );
   }

   /**
    * This method will reset all class variables to their defaults
    */

   private void resetDefaultValues() {
      password = "";
      username = "anonymous";
      server = "";
      port = -1;
      endpoint = "";
   }

   /**
    * This will return null in case the location was successfully parsed, class variables server/port/endpoint will then
    * be set based on the contents of the location field
    * 
    * @return
    */

   private String parseLocationField() {
      String location = locationField.getText();

      // should look like:
      // <server>:<port>/<endpoint>
      Pattern serverLocation = Pattern.compile( "(.+?)(:([\\d]+))?(/(.*))" );
      Matcher m = serverLocation.matcher( location );
      if( !m.find() || m.groupCount() != 5 )
         return "syntax not recognized";

      server = m.group( 1 );
      try {
         port = Integer.parseInt( m.group( 3 ) );
      }
      catch( Exception e ) {
         port = 80;
      }
      endpoint = m.group( 4 );

      return null;
   }

   public void actionPerformed( ActionEvent event ) {
      if( event.getSource() == BTN_CANCEL ) {
         isCanceled = true;
         setVisible( false );
      } else if( event.getSource() == BTN_OK ) {
         try {
            getContentPane().setEnabled( false );

            // show waiting cursor
            setCursor( new Cursor( Cursor.WAIT_CURSOR ) );

            // parse the server location field
            String parseResult = parseLocationField();
            if( parseResult != null ) {
               Application.showErrorMessage( "Location error", "Server location field incorrect: " + parseResult );
               // reset the fields
               resetDefaultValues();
               return;
            }

            // setup the sessionManager
            try {
               sessionManager.setupServerConnection( server, port, endpoint );
            }
            catch( Exception e ) {
               Application.showErrorMessage( "Server connection error", e.getMessage() );
               // reset the fields
               resetDefaultValues();
               return;
            }

            username = usernameField.getText();
            password = new String( passwordField.getPassword() );

            // try to login with username/password
            if( !sessionManager.login( username, password ) ) {
               Application.showErrorMessage( "Login error", "Could not login: "
                  + sessionManager.getLastCommunicationException().getMessage() );

               // reset the fields
               resetDefaultValues();
               setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) );
               return;
            }

            setVisible( false );

            try {
               DatasetSelectionDialog datasetDialog =
                  new DatasetSelectionDialog( ( (JFrame)this.getOwner() ), sessionManager );
               datasetDialog.setVisible( true );

               if( datasetDialog.isCanceled() ) {
                  resetDefaultValues();

                  sessionManager.shutdown();

                  setVisible( false );
               } else {
                  selectedDataset = datasetDialog.getSelectedDataset();
               }
            }

            catch( Exception e ) {
               e.printStackTrace();
            }

            sessionManager.addConnectionStateListeners( gui );

         }
         finally {
            // reset the cursor
            setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) );

            getContentPane().setEnabled( true );
         }
      }
   }

   public String getPassword() {
      return password;
   }

   public String getUsername() {
      return username;
   }

   public void keyPressed( KeyEvent e ) {
   }

   public void keyReleased( KeyEvent e ) {
      // KeyListener was added for the username and password fields, both should trigger the button_ok
      if( e.getSource() == usernameField || e.getSource() == passwordField || e.getSource() == locationField ) {
         if( e.getKeyCode() == KeyEvent.VK_ENTER ) {
            BTN_OK.doClick();
            e.consume();
         }
      }
   }

   public Dataset getSelectedDataset() {
      return selectedDataset;
   }

   public void keyTyped( KeyEvent e ) {
   }

   public boolean isCanceled() {
      return isCanceled;
   }

}
